package cn.com.bluemoon.daps.common.datascope.interceptor;

import cn.com.bluemoon.daps.common.constant.DapConstant;
import cn.com.bluemoon.daps.common.datascope.BmPermission;
import cn.com.bluemoon.daps.common.datascope.IgnorePermission;
import cn.com.bluemoon.daps.common.datascope.log.holder.ProcessLogHolder;
import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.domain.UserInfoHolder;
import cn.com.bluemoon.daps.common.exception.DapThrowException;
import cn.com.bluemoon.daps.domp.api.IDompService;
import cn.com.bluemoon.daps.domp.api.LocalDompService;
import cn.com.bluemoon.daps.domp.api.LocalUserService;
import cn.com.bluemoon.daps.domp.api.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * web auth 拦截
 * token认证
 *
 * @author Jarod.Kong
 */
@Slf4j
public class WebPermissionRequestInterceptor extends HandlerInterceptorAdapter {
    private final LocalUserService userService;
    private final LocalDompService dompService;

    public WebPermissionRequestInterceptor(LocalUserService userService, LocalDompService dompService) {
        this.userService = userService;
        this.dompService = dompService;
    }

    /**
     * 1.token无效，http 401, 跳转到登录页面
     * 2.token有效，会刷新有效时间
     * 2.1.没权限，http 403
     * 2.2.放权,http 200/500
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "*");
//                response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
        //检测是options方法则直接返回200(否则报跨越问题)
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }
        BmPermission bmPermission;
        // 获取用户菜单信息，和请求url信息是否有效
        if (handler instanceof HandlerMethod) {
            try {
                log.info("进行对请求接口{}权限校验...", request.getRequestURI());
                // 1判断该接口是否为完全开放
                IgnorePermission ignorePermission = ((HandlerMethod) handler).getMethodAnnotation(IgnorePermission.class);
                IgnorePermission clzPermission = null;
                if (ignorePermission != null) {
                    log.warn("该请求{},name=[{}]无需进行token认证可直接放行", request.getRequestURI(), ignorePermission.name());
                    UserInfoHolder.setGuestUserInfo();
                    return true;
                } else if ((clzPermission = ((HandlerMethod) handler).getBeanType().getDeclaredAnnotation(IgnorePermission.class)) != null) {
                    log.warn("该请求{},name=[{}]无需进行token认证可直接放行", request.getRequestURI(), clzPermission.name());
                    UserInfoHolder.setGuestUserInfo();
                    return true;
                }
                // 2非完全开放的处理 fix 增加获取类注解
                bmPermission = AnnotationUtils.findAnnotation(((HandlerMethod) handler).getMethod(), BmPermission.class);
                if (bmPermission == null) {
                    bmPermission = AnnotationUtils.findAnnotation(((HandlerMethod) handler).getMethod().getDeclaringClass(), BmPermission.class);
                }
                if (bmPermission == null) {
                    log.error("请求接口{}方法未注册相关权限配置，放行", request.getRequestURI());
                    UserInfoHolder.setGuestUserInfo();
                    return true;
                } else {
                    // 针对cn.com.bluemoon.daps.common.datascope.BmPermission.queryTokenPerm情况调整
                    String paramTokenValue = request.getParameter(DapConstant.WEB_HTTP_TOKEN_NAME);
                    String token = (bmPermission.queryTokenPerm() && StringUtils.isNotBlank(paramTokenValue)) ? paramTokenValue
                            : request.getHeader(DapConstant.WEB_HTTP_TOKEN_NAME);
                    // 获取用户，若用户有效则会刷新token有效时间
                    String userAccount = userService.isLoginUser(token);
                    if (token != null && userAccount != null) {
                        // 放行，获取用户信息
                        UserInfoHolder.UserInfo userInfoById = userService.getUserInfoByUserId(userAccount);
                        userInfoById.setToken(token);
                        UserInfoHolder.setUserInfo(userInfoById);
                    } else {
                        // 目前不对该参数进行权限控制，后续待用户与角色明确后，进行放权控制
                        log.error("请求参数中不存在token信息或者token无效，不放行");
                        response.setStatus(HttpStatus.FORBIDDEN.value());
                        throw new DapThrowException(ResultBean.FORBIDDEN.getMsg() + "：" + bmPermission.name(), ResultBean.FORBIDDEN.getCode());
                    }
                    // 是否直接放行 true：说明该接口放行，不进行用户的角色和菜单权限验证
                    if (bmPermission.ignore()) {
                        log.warn("请求接口{}对外开放，用户信息为{}", request.getRequestURI(), UserInfoHolder.getUserInfoOrEmpty().getAccount());
                        return true;
                    } else {
                        String[] values = bmPermission.value();
                        // 获取token对应用户的菜单权限
                        Map<String, List<LocalDompService.MenuDto>> menuPaths = dompService.getMenuPathByTokenAndUserId(token, userAccount);
                        boolean anyMatch = Arrays.stream(values).anyMatch(menuPaths::containsKey);
                        if (!anyMatch) {
                            log.info("用户：{}无权访问接口标记：{}=>[{}]", userAccount, bmPermission.name(), Arrays.toString(values));
                            response.setStatus(HttpStatus.UNAUTHORIZED.value());
                            throw new DapThrowException(ResultBean.UNAUTHORIZED.getMsg() + "：" + bmPermission.name() + "功能", ResultBean.UNAUTHORIZED.getCode());
                        }
                    }
                }
            } catch (RuntimeException e) {
                ProcessLogHolder.set(e.getMessage(), true, UserInfoHolder.getUserInfoOrEmpty());
                throw e;
            }
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
        UserInfoHolder.clearUserInfo();
    }
}
